package com.tree.thrive.business.el.aspect;

import com.tree.thrive.business.el.Lock;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * top-tree
 *
 * @Auther: cheng.tang
 * @Date: 2022/7/12 2:58 PM
 * @Description:
 */
@Slf4j
@Aspect
@Component
public class LockAspect {

    @Pointcut("@annotation(lock)")
    public void lockPointCut(Lock lock) {
    }

    @Around("lockPointCut(lock)")
    private Object advice(ProceedingJoinPoint joinPoint, Lock lock) throws Throwable {
        Method method = getMethod(joinPoint);
        String lockPath = parseLockPath(joinPoint, method, lock);
        log.info(" lockPath {} ", lockPath);
        return joinPoint.proceed();
    }

    private String parseLockPath(ProceedingJoinPoint joinPoint, Method method, Lock lockAnnotation) {

        String lockPath = lockAnnotation.path();

        String redisSeparator = ":";
        if (StringUtils.isBlank(lockPath)) {
            lockPath = joinPoint.getTarget().getClass().getSimpleName() + redisSeparator + method.getName();
        }

        if (StringUtils.isNotBlank(lockPath)) {
            lockPath = parseKey(lockAnnotation, method, joinPoint.getArgs());
        }

        lockPath = "Lock" + redisSeparator + lockPath;
        log.info("parseLockPath result : {} ", lockPath);
        return lockPath;
    }

    private String parseKey(Lock lock, Method method, Object[] args) {
        /**
         * 获取被拦截方法参数名列表(使用Spring支持类库)
         */
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);
        if (paraNameArr == null || paraNameArr.length == 0) {
            return lock.path();
        }

        /**
         * 使用SPEL进行key的解析
         */
        SpelExpressionParser parser = new SpelExpressionParser();
        /**
         * SPEL上下文
         */
        StandardEvaluationContext context = new StandardEvaluationContext();
        /**
         * 把方法参数放入SPEL上下文中
         */
        for (int i = 0; i < paraNameArr.length; i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }
        String springElResult = parser.parseExpression(lock.path()).getValue(context, String.class);
        log.info("springElResult {} ", springElResult);
        return springElResult;
    }

    private Method getMethod(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        if (method.getDeclaringClass().isInterface()) {
            try {
                String methodSignatureName = methodSignature.getName();
                Class[] parameterTypes = methodSignature.getParameterTypes();
                log.info(" methodSignatureName {} {} ", methodSignatureName, Arrays.stream(parameterTypes).collect(Collectors.toList()));
                return joinPoint.getTarget().getClass().getMethod(methodSignatureName, parameterTypes);
            } catch (Exception e) {
                log.warn(" {} ", e.getMessage(), e);
            }
        }
        return method;
    }

}
